#pragma once

namespace AVUI {

class MeshGeometry3D;

class HitTestResult : public Object
{
public:
    HitTestResult(DependencyObject* pVisualHit) { m_pVisualHit = pVisualHit; }

    DECLARE_ELEMENT(HitTestResult, Object);

    DependencyObject* get_VisualHit() { return m_pVisualHit; }

private:

    DependencyObject* m_pVisualHit;
};

class PointHitTestResult : public HitTestResult
{
public:
    PointHitTestResult(DependencyObject* pVisualHit, const Point& ptHit) : HitTestResult(pVisualHit), m_pointHit(ptHit) { }

    DECLARE_ELEMENT(PointHitTestResult, HitTestResult);

    Point get_PointHit() { return m_pointHit; }

private:
    Point m_pointHit;
};

class RayHitTestResult : public HitTestResult
{
public:
    DECLARE_ELEMENT(RayHitTestResult, HitTestResult);

    RayHitTestResult(Visual3D* pVisualHit, Model3D* pModelHit) : HitTestResult(pVisualHit), m_pModelHit(pModelHit) { };
    static int CompareByDistanceToRayOrigin(RayHitTestResult* pX, RayHitTestResult* pY)
    {
        return Float::Sign(pX->get_DistanceToRayOrigin() - pY->get_DistanceToRayOrigin());
    }
    virtual void SetDistanceToRayOrigin(float distance) = 0;

    virtual float get_DistanceToRayOrigin() = 0;
    Model3D* get_ModelHit() { return m_pModelHit; }
    virtual Point3D get_PointHit() = 0;
    Visual3D* get_VisualHit() { return (Visual3D*) HitTestResult::get_VisualHit(); }

private:

    TRefCountedPtr<Model3D> m_pModelHit;
};

class RayMeshGeometry3DHitTestResult : public RayHitTestResult
{
public:
    DECLARE_ELEMENT(RayMeshGeometry3DHitTestResult, RayHitTestResult);

    RayMeshGeometry3DHitTestResult(Visual3D* pVisualHit, Model3D* pModelHit, MeshGeometry3D* pMeshHit, const Point3D& pointHit, float distanceToRayOrigin, int vertexIndex1, int vertexIndex2, int vertexIndex3, const Point& barycentricCoordinate) : RayHitTestResult(pVisualHit, pModelHit)
    {
        m_pMeshHit = pMeshHit;
        m_pointHit = pointHit;
        m_distanceToRayOrigin = distanceToRayOrigin;
        m_vertexIndex1 = vertexIndex1;
        m_vertexIndex2 = vertexIndex2;
        m_vertexIndex3 = vertexIndex3;
        m_barycentricCoordinate = barycentricCoordinate;
    }

    virtual void SetDistanceToRayOrigin(float distance) AVUI_OVERRIDE
    {
        m_distanceToRayOrigin = distance;
    }

    virtual float get_DistanceToRayOrigin() AVUI_OVERRIDE { return m_distanceToRayOrigin; }
    MeshGeometry3D* get_MeshHit() { return m_pMeshHit; } 
    Point3D get_PointHit() { return m_pointHit; }
    int get_VertexIndex1() { return m_vertexIndex1; }
    int get_VertexIndex2() { return m_vertexIndex2; }
    int get_VertexIndex3() { return m_vertexIndex3; }
    float get_VertexWeight1() { return ((1.0f - get_VertexWeight2()) - get_VertexWeight3()); }
    float get_VertexWeight2() { return m_barycentricCoordinate.get_X(); }
    float get_VertexWeight3() { return m_barycentricCoordinate.get_Y(); }

private:
 
    Point m_barycentricCoordinate;
    float m_distanceToRayOrigin;
    TRefCountedPtr<MeshGeometry3D> m_pMeshHit;
    Point3D m_pointHit;
    int m_vertexIndex1;
    int m_vertexIndex2;
    int m_vertexIndex3;
};

 


}; // namespace AVUI


